package com.rolfhoward.video.record.manager

import android.content.Context
import android.media.*
import android.os.Build
import android.os.Environment
import android.text.TextUtils
import android.util.Log
import java.io.File
import java.nio.ByteBuffer

/**
 *@author wuqiubin
 *@date 2022/10/18
 *@Describe 视频编辑工具，分离MP4文件的音视频，合成无声MP4文件与aac音频文件
 */
class VideoEditManager private constructor(){

    private val TAG = VideoEditManager.javaClass.name

    companion object{
        private var instance: VideoEditManager? = null
        fun getInstance(): VideoEditManager {
            instance ?: synchronized(this){
                instance ?: VideoEditManager().also { instance = it }
            }
            return instance!!
        }
    }

    /**
     * 提取音视频轨道-解封装
     * 提取数据
     * @param context 上下文
     * @param path 视频文件路径
     * */
    fun onMediaExtractor(context: Context, path: String, callback: (Int, String) -> Unit){
        //获取源文件
        var sourceFile = File(path)
        if (!sourceFile.exists()){
            Log.i(TAG, "视频源文件不存在")
            callback(-1, "视频源文件不存在")
            return
        }
        callback(0, "正在解封装视频")
        //构造MediaExtractor对象
        var mediaExtractor = MediaExtractor()
        try {
            //设置数据源
            mediaExtractor.setDataSource(path)
            //获取轨道数据
            var trackCount = mediaExtractor.trackCount;
            //遍历轨道信息，获取音频或视频轨道信息
            for (i in 0 until trackCount){
                //获取某一轨道的媒体格式信息
                var trackFormat = mediaExtractor.getTrackFormat(i)
                var keyMime = trackFormat.getString(MediaFormat.KEY_MIME)
                if (TextUtils.isEmpty(keyMime)){
                    continue
                }
                var filename = path.substring(path.indexOfLast { it == '/' } , path.indexOfLast { it == '.'})
                //通过mime信息识别音轨或视频轨道
                if (keyMime!!.startsWith("video/")){
                    //合成视频信息
                    onMediaExtractorMuxer(context, mediaExtractor, i, "/$filename.mp4")
                }else if (keyMime.startsWith("audio/")){
                    //合成音频信息
                    onMediaExtractorMuxer(context, mediaExtractor, i, "/$filename.aac")
                }
            }
            callback(3, path)
        }catch (e:Exception){
            e.printStackTrace()
        }finally {
            mediaExtractor.release()
        }
    }

    /**
     * 音视频合成 - 封装
     * 把音轨和视频轨道合成封装为新的视频
     * */
    private fun onMediaExtractorMuxer(context: Context, mediaExtractor: MediaExtractor, i: Int, outputName:String){
        var outputFile = File(context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)?.absolutePath+outputName)
        if (outputFile.exists()){
            outputFile.delete()
        }
        var trackFormat = mediaExtractor.getTrackFormat(i)

        //  视频旋转顺时针角度
        //  视频旋转顺时针角度
        var rotation = -1
        if (trackFormat.containsKey(MediaFormat.KEY_ROTATION)) {
            rotation = trackFormat.getInteger(MediaFormat.KEY_ROTATION)
        }
        // 比特率模式
        // 比特率模式
        var bitRateMode = -1
        if (trackFormat.containsKey(MediaFormat.KEY_BITRATE_MODE)) {
            bitRateMode = trackFormat.getInteger(MediaFormat.KEY_BITRATE_MODE)
        }
        println("rotation:$rotation, bitRateMode:$bitRateMode")

        mediaExtractor.selectTrack(i)
        //构造MediaMuxer对象
        var mediaMuxer = MediaMuxer(outputFile.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
        //添加轨道信息 参数为MediaFormat
        mediaMuxer.addTrack(trackFormat)
        //开始合成
        mediaMuxer.start()

        //设置buffer
        var buffer = ByteBuffer.allocate(500*1024)
        var bufferInfo = MediaCodec.BufferInfo()

        var sampleSize: Int
        //通过mediaExtractor.readSampleData读取数据流
        while (mediaExtractor.readSampleData(buffer, 0).also { sampleSize = it }>0){
            bufferInfo.flags = mediaExtractor.sampleFlags
            bufferInfo.offset = 0
            bufferInfo.size = sampleSize
            bufferInfo.presentationTimeUs = mediaExtractor.sampleTime
            bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM
            mediaMuxer.writeSampleData(0, buffer, bufferInfo)
            mediaExtractor.advance()
        }
        mediaExtractor.unselectTrack(i)
        mediaMuxer.stop()
        mediaMuxer.release()
    }

    /**
     * 把音轨和视频轨道合成封装为新的视频
     * 流程：解封装 -> 解码 -> 再编码 -> 封装
     * 1、使用MediaExtractor解封装mp4获得h246流
     * 2、使用MediaMuxer将h264封装成mp4
     * @param context 上下文
     * @param inputAudio 需输入音频文件地址 aac文件
     * @param inputVideo 需输入视频文件地址 mp4文件
     * @param callback 操作回调
     * */
    fun onMediaMuxer(context: Context, inputAudio:String, inputVideo:String, callback:(Int, String) -> Unit){
        var audioFile = File(inputAudio)
        if (!audioFile.exists()){
            Log.i(TAG, "音频源文件不存在")
            callback(-1, "音频源文件不存在")
            return
        }
        var videoFile = File(inputVideo)
        if (!videoFile.exists()){
            Log.i(TAG, "视频源文件不存在")
            callback(-1, "视频源文件不存在")
            return
        }
        callback(0, "正在合成")
        var outputFile = File(context.getExternalFilesDir(null)?.absolutePath+"/newVideo.mp4")
        if (outputFile.exists()){
            outputFile.delete()
        }
        try {
            //生成MediaMuxer对象，指定视频文件输出路径和文件格式
            var mediaMuxer = MediaMuxer(outputFile.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
            //添加视频轨道信息
            var videoExtractor = MediaExtractor()
            videoExtractor.setDataSource(videoFile.absolutePath)
            var trackCountVideo = videoExtractor.trackCount
            var videoTrackIndex = 0
            for (i in 0 until trackCountVideo){
                var trackFormat = videoExtractor.getTrackFormat(i)
                var mimeType = trackFormat.getString(MediaFormat.KEY_MIME)
                if (TextUtils.isEmpty(mimeType)){
                    continue
                }
                if (mimeType!!.startsWith("video/")){
                   videoExtractor.selectTrack(i)
                    //把MediaFormat添加到MediaMuxer后记录返回的track index，添加完所有track后调用start方法
                   videoTrackIndex = mediaMuxer.addTrack(trackFormat)  //添加轨道
                   break
                }
            }

            //添加音频轨道信息
            var audioTrackIndex = 0
            var audioExtractor = MediaExtractor()
            audioExtractor.setDataSource(audioFile.absolutePath)
            var trackCountAudio = audioExtractor.trackCount
            for (i in 0 until trackCountAudio){
                var trackFormat = audioExtractor.getTrackFormat(i)
                var mimeType = trackFormat.getString(MediaFormat.KEY_MIME)
                if (TextUtils.isEmpty(mimeType)){
                    continue
                }
                if (mimeType!!.startsWith("audio/")){
                    audioExtractor.selectTrack(i)
                    audioTrackIndex = mediaMuxer.addTrack(trackFormat)
                    break
                }
            }

            //合成音视频轨道信息
            mediaMuxer.start()
            var byteBuffer = ByteBuffer.allocate(500*1024)
            var bufferInfo = MediaCodec.BufferInfo()
            var sampleSize:Int
            //把指定通道中的数据按偏移量读取到ByteBuffer中
            while (videoExtractor.readSampleData(byteBuffer, 0).also { sampleSize = it }>0){
                //设置BufferInfo对象值
                bufferInfo.flags = videoExtractor.sampleFlags
                bufferInfo.offset = 0
                bufferInfo.size = sampleSize
                bufferInfo.presentationTimeUs = videoExtractor.sampleTime
                //调用MediaMuxer.writeSampleData()向mp4文件中写入数据
                mediaMuxer.writeSampleData(videoTrackIndex, byteBuffer, bufferInfo)
                //读取下一帧数据
                videoExtractor.advance()
            }

            var audioSampleSize:Int
            var audioBufferInfo = MediaCodec.BufferInfo()
            //把指定通道中的数据按偏移量读取到ByteBuffer中
            while (audioExtractor.readSampleData(byteBuffer, 0).also { audioSampleSize = it }>0){
                //设置BufferInfo对象值
                audioBufferInfo.flags = audioExtractor.sampleFlags
                audioBufferInfo.offset = 0
                audioBufferInfo.size = audioSampleSize
                audioBufferInfo.presentationTimeUs = audioExtractor.sampleTime
                //调用MediaMuxer.writeSampleData()向mp4文件中写入数据
                mediaMuxer.writeSampleData(audioTrackIndex, byteBuffer, audioBufferInfo)
                //读取下一帧数据
                audioExtractor.advance()
            }

            //释放资源
            videoExtractor.release()
            audioExtractor.release()
            mediaMuxer.stop()
            mediaMuxer.release()
            callback(3, "")
        }catch (e:Exception){
            e.printStackTrace()
        }
    }

    private fun configEncoderWithVBR(codec: MediaCodec, outputFormat: MediaFormat) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            outputFormat.setInteger(
                MediaFormat.KEY_BITRATE_MODE,
                MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR
            )
        }
        codec.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
    }
}